/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2013 All Rights Reserved.
 */
package com.pingan.mobilegw.biz.shared.router.executor;

import com.alibaba.common.lang.StringUtil;
import com.alibaba.fastjson.JSON;
import com.pingan.mobilegw.biz.shared.router.executor.ParallelBatchExecutor.SingleResponse;
import com.pingan.mobilegw.core.model.GatewayRequest;
import com.pingan.mobilegw.core.model.request.SingleRequestBody;
import com.pingan.mobilegw.core.model.response.SingleResponseBody;
import com.pingan.mobilegw.mobileservice.invoke.MobileRpcHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 批量请求响应类
 * 
 * 非线程安全类，务必每个http请求实例化一个该对象
 * 
 * @author jiadao
 * @version $Id: BatchResponseRender.java, v 0.1 2013-10-18 下午2:40:20 jiadao Exp $
 */
public class BatchResponseRender {


    /** 默认logger */
    private static final Log logger              = LogFactory
                                                         .getLog(BatchResponseRender.class);

    /** 登陆operationType */
    private static final String  LOGIN_OPERATIONTYPE = "alipay.user.login";

    /** 字符集 */
    private static final String  CHARSET             = "UTF-8";

    /** http响应 */
    private HttpServletResponse  response;

    /** 数据长度byte */
    private byte[]               sizeBuf             = new byte[4];

    /** batch请求中时候有登陆 */
    private boolean              hasLogin;

    /** 登陆请求id */
    private int                  loginRequestId      = -1;

    /** 是否已经处理过。。 */
    private boolean              hasProcessLogin     = false;

    /** 等待flush的请求buffer */
    private List<SingleResponse> waitFlushBuffer     = new ArrayList<SingleResponse>();

    /**
     * 构造函数
     * 
     * @param response
     * @param singleRequests
     */
    public BatchResponseRender(HttpServletResponse response, List<GatewayRequest> singleRequests) {
        this.response = response;
        this.response.setCharacterEncoding("utf-8");
        this.response.setHeader("Content-type", "text/html;charset=utf-8");
        this.response.setHeader("X-Accel-Buffering", "no");

        for (GatewayRequest request : singleRequests) {
            SingleRequestBody requestBody = (SingleRequestBody) request.getRequestBody();
            if (StringUtil.equals(requestBody.getOperationType(), LOGIN_OPERATIONTYPE)) {
                hasLogin = true;
                loginRequestId = requestBody.getId();
            }
        }
    }

    /**
     * flush响应body
     * 
     * @param singleResponse
     */
    public void flushResponseBody(SingleResponse singleResponse) {
        SingleResponseBody body = (SingleResponseBody) singleResponse.getResponse()
            .getResponseBody();

        if (hasLogin) {
            if (!hasProcessLogin) {
                // 请求中有登陆，并且当前返回结果不是登陆，缓存起响应
                if (body.getId() != loginRequestId) {
                    waitFlushBuffer.add(singleResponse);
                } else {
                    // 是登陆请求
                    // flush 数据
                    this.flushData(singleResponse);
                    hasProcessLogin = true;
                }
            } else {
                // 1. 如果已经处理过登陆
                // 2. 把之前缓存的响应flush出去，并且最后把缓存清除。
                // 3. flush当前的这个请求
                if (!CollectionUtils.isEmpty(waitFlushBuffer)) {
                    for (SingleResponse r : waitFlushBuffer) {
                        this.flushData(r);
                    }
                    waitFlushBuffer.clear();
                }

                this.flushResponseBody(singleResponse);
            }
        } else {
            this.flushData(singleResponse);
        }

    }

    /**
     *  结束响应body
     */
    public void endResponseBody() {
        try {
            encodeSize(0, sizeBuf);
            response.getOutputStream().write(sizeBuf);
        } catch (IOException e) {
            logger.error("output stream write error!", e);
        }
    }

    /**
     * 具体的flush数据动作
     * 
     * @param singleResponse
     */
    private void flushData(SingleResponse singleResponse) {
        try {
            String data = JSON.toJSONString(singleResponse.getResponse().getResponseBody());

            byte[] dataBuf = data.getBytes(CHARSET);
            int dataSize = dataBuf.length;

            encodeSize(dataSize, sizeBuf);
            response.getOutputStream().write(sizeBuf);
            response.getOutputStream().write(dataBuf);
            response.flushBuffer();

            saveContext(singleResponse);

        } catch (IOException e) {
            logger.error("output stream write error", e);
        }

    }

    /**
     * 保存每次响应的数据体
     * 
     * @param sr
     */
    private void saveContext(SingleResponse sr) {

        // session merge
        if (!CollectionUtils.isEmpty(sr.getIncrementalChanges())) {
            MobileRpcHolder.getIncrementalChanges().putAll(sr.getIncrementalChanges());
        }

    }

    /**
     * 编码size
     * 
     * @param size
     * @param buf
     */
    private static final void encodeSize(final int size, final byte[] buf) {
        buf[0] = (byte) (0xff & (size >> 24));
        buf[1] = (byte) (0xff & (size >> 16));
        buf[2] = (byte) (0xff & (size >> 8));
        buf[3] = (byte) (0xff & (size));
    }

}
